using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace CodeStory.Core.Services
{
	/// <summary>
	/// 
	/// </summary>
	public static class ObjectPersisteService
	{
		/// <summary>
		/// Serialize the object as key-value.
		/// </summary>
		/// <param name="persisteObject">The object to persist.</param>
		/// <returns>The key-value dictionary of the object.</returns>
		public static Dictionary<string, string> SaveObject(object persisteObject) {
			Dictionary<string, string> result = new Dictionary<string, string>();
			PropertyDescriptorCollection props = TypeDescriptor.GetProperties(persisteObject);
			PersisteObject(props,result,persisteObject,string.Empty);
			return result;
		}
		/// <summary>
		/// Deserialize the object from the key-value dictionary.
		/// </summary>
		/// <param name="dictionary"></param>
		/// <param name="loadedObject"></param>
		/// <returns></returns>
		public static object LoadObject(Dictionary<string, string> dictionary,object loadedObject) {
			PropertyDescriptorCollection props = TypeDescriptor.GetProperties(loadedObject);
			LoadObjectValue(loadedObject, dictionary, props, "");
			return loadedObject;
		}
		/// <summary>
		/// Loads the object value.
		/// </summary>
		/// <param name="valueObject">The value object.</param>
		/// <param name="dict">The dict.</param>
		/// <param name="props">The props.</param>
		/// <param name="valuepath">The valuepath.</param>
		private static void LoadObjectValue(object valueObject, Dictionary<string, string> dict, PropertyDescriptorCollection props, string valuepath) {
			foreach (PropertyDescriptor descriptor in props) {
				string name = Escape(descriptor.Name);
				string currentValuePath = name;
				if (!string.IsNullOrEmpty(valuepath)) {
					currentValuePath = string.Format("{0}\\{1}", valuepath, name);
				}
				if (dict.ContainsKey(currentValuePath)) {
					object val = dict[currentValuePath];
					TypeConverter con = descriptor.Converter;
					if (con.CanConvertFrom(typeof(string))) {
						descriptor.SetValue(valueObject, con.ConvertFromString(val.ToString()));
					}
				}
				object propertyValue = null;
				try {
					propertyValue = descriptor.GetValue(valueObject);
				} catch {
				}
				if (descriptor.Converter.GetPropertiesSupported()) {
					PropertyDescriptorCollection subProps = descriptor.Converter.GetProperties(propertyValue);
					LoadObjectValue(propertyValue, dict, subProps, currentValuePath);
				}
			}
		}

		/// <summary>
		/// Persistes the object.
		/// </summary>
		/// <param name="props">The props.</param>
		/// <param name="result">The result.</param>
		/// <param name="valueObject">The value object.</param>
		/// <param name="path">The path.</param>
		private static void PersisteObject(PropertyDescriptorCollection props, Dictionary<string,string> result, object valueObject,string path) {
			props.Sort();
			foreach (PropertyDescriptor descriptor in props) {
				//
				string name = Escape(descriptor.Name);
				string innerpath = name;
				if (!string.IsNullOrEmpty(path)) {
					innerpath = string.Format("{0}\\{1}",path,innerpath);
				}
				object propertyValue = null;
				try {
					propertyValue = descriptor.GetValue(valueObject);
				} catch {
				}
				string valueString;
				if (propertyValue != null) {
					if (descriptor.Converter.CanConvertTo(typeof(string))) {
						valueString = descriptor.Converter.ConvertToString(propertyValue);
					} else {
						valueString = propertyValue.ToString();
					}
				} else {
					valueString = string.Empty;
				}
				result[innerpath] = valueString;

				if (descriptor.Converter.GetPropertiesSupported()) {
					PropertyDescriptorCollection subProps = descriptor.Converter.GetProperties(propertyValue);
					PersisteObject(subProps, result, propertyValue,innerpath);
				}
			}
		}

		/// <summary>
		/// Escapes the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns></returns>
		private static string Escape(string value) {
			StringBuilder builder = null;
			if (string.IsNullOrEmpty(value)) {
				return string.Empty;
			}
			int startIndex = 0;
			int count = 0;
			for (int i = 0; i < value.Length; i++) {
				switch (value[i]) {
					case '\\':
						if (builder == null) {
							builder = new StringBuilder(value.Length + 5);
						}
						if (count > 0) {
							builder.Append(value, startIndex, count);
						}
						builder.Append("*|*");
						startIndex = i + 1;
						count = 0;
						break;
					case '|':
						if (builder == null) {
							builder = new StringBuilder(value.Length + 5);
						}
						if (count > 0) {
							builder.Append(value, startIndex, count);
						}
						builder.Append("||");
						startIndex = i + 1;
						count = 0;
						break;
					default:
						count++;
						break;
				}
			}
			if (builder == null) {
				return value;
			}
			if (count > 0) {
				builder.Append(value, startIndex, count);
			}
			return builder.ToString();
		}
	}
}
